package com.auto.base.factory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.reflection.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.sql.Array;
import java.sql.SQLException;
import java.util.*;

public class JdbcTemplate extends org.springframework.jdbc.core.JdbcTemplate {

    protected static final Log logger = LogFactory.getLog(JdbcTemplate.class);


    public JdbcTemplate(DriverManagerDataSource dataSource) {
        super(dataSource);
    }

    public int update(@NotNull String sql, @Nullable Object... args) throws DataAccessException {
        logger.info(" Preparing: " + removeBreakingWhitespace(sql));
        logger.info("Parameters: " + getParameterValueString(Arrays.asList(Objects.requireNonNull(args))));
        return update(sql, newArgPreparedStatementSetter(args));
    }


    public <T> List<T> query( @NotNull String sql,  @NotNull Class<T> clazz, @Nullable Object... args) throws DataAccessException {
        RowMapper<T> rowMapper = new BeanPropertyRowMapper<>(clazz);
        return result(query(sql, args, new RowMapperResultSetExtractor<>(rowMapper)));
    }

    @Override
    @Nullable
    public <T> T queryForObject(@NotNull String sql, @NotNull Class<T> clazz, @Nullable Object... args) throws DataAccessException {
        RowMapper<T> rowMapper = new BeanPropertyRowMapper<>(clazz);
        List<T> results = query(sql, args, new RowMapperResultSetExtractor<>(rowMapper, 1));
        return DataAccessUtils.nullableSingleResult(results);
    }


    private static <T> T result(@Nullable T result) {
        Assert.state(result != null, "No result");
        return result;
    }

    private static String removeBreakingWhitespace(String original) {
        StringTokenizer whitespaceStripper = new StringTokenizer(original);
        StringBuilder builder = new StringBuilder();
        while (whitespaceStripper.hasMoreTokens()) {
            builder.append(whitespaceStripper.nextToken());
            builder.append(" ");
        }
        return builder.toString();
    }

    private static String getParameterValueString(List<Object> columnValues) {
        List<Object> typeList = new ArrayList<>(columnValues.size());
        for (Object value : columnValues) {
            if (value == null) {
                typeList.add("null");
            } else {
                typeList.add(objectValueString(value) + "(" + value.getClass().getSimpleName() + ")");
            }
        }
        final String parameters = typeList.toString();
        return parameters.substring(1, parameters.length() - 1);
    }

    private static String objectValueString(Object value) {
        if (value instanceof Array) {
            try {
                return ArrayUtil.toString(((Array) value).getArray());
            } catch (SQLException e) {
                return value.toString();
            }
        }
        return value.toString();
    }
}
